package com.wh1200.mybatis.xmlless.resolver

import com.wh1200.jackson.createObjectMapper
import com.wh1200.mybatis.xmlless.inter.ITypeResolver
import com.wh1200.mybatis.xmlless.model.FieldMapping
import com.fasterxml.jackson.databind.JavaType
import org.apache.ibatis.type.DoubleTypeHandler
import org.apache.ibatis.type.LongTypeHandler
import org.apache.ibatis.type.TypeHandler
import java.lang.reflect.*

/**
 * 类型解析
 * @author 吴昊
 * @since 0.0.7
 */
object TypeResolver {

  private lateinit var resolver: ITypeResolver

  fun registerResolver(resolver: ITypeResolver) {
    this.resolver = resolver
  }

  fun resolveReturnJavaType(method: Method, clazz: Class<*>, forceSingleValue: Boolean = false): JavaType? {
    return resolver.resolveReturnJavaType(method, clazz, forceSingleValue)
  }

  fun resolveReturnMappingType(method: Method, clazz: Class<*>): Class<*> {
    return resolver.resolveReturnMappingType(method, clazz) ?: error("无法解析方法${method}的返回值")
  }

  fun resolveNullableReturnMappingType(method: Method, clazz: Class<*>): Class<*>? {
    return resolver.resolveReturnMappingType(method, clazz)
  }

  /**
   * 解析带单个泛型参数的类型的泛型类型，如果没有泛型则返回参数类型
   */
  @Throws(IllegalStateException::class)
  fun resolveRealType(type: Type): Class<*> {
    return when (type) {
      is Class<*>          -> type
      is ParameterizedType -> {
        val typeArg = type.actualTypeArguments[0]
        if (typeArg is WildcardType) {
          typeArg.upperBounds[0] as Class<*>
        } else if (typeArg is ParameterizedType) {
          typeArg.rawType as Class<*>
        } else {
          typeArg as Class<*>
        }
      }
      else                 -> error("无法确定${type}的类型")
    }
  }

  fun toJavaType(type: Type): JavaType? {
    val typeFactory = createObjectMapper().typeFactory
    return typeFactory.constructType(type)
  }

  fun resolveTypeHandler(fieldMapping: FieldMapping): TypeHandler<*>? {
    val handlerAnno = fieldMapping.handler
    if (handlerAnno != null) {
      return handlerAnno.constructors.first { it.parameterCount == 0 }.newInstance() as TypeHandler<*>
    }
    if (fieldMapping.isJsonObject) {
      val clazz = Class.forName("com.wh1200.mybatis.xmlless.handler.JsonTypeHandler")
      return clazz.constructors.first { it.parameterCount == 1 }
          .newInstance(toJavaType(fieldMapping.type)) as TypeHandler<*>
    }
    if (fieldMapping.type == Double::class.java) {
      return DoubleTypeHandler()
    }
    if (fieldMapping.type == Long::class.java) {
      return LongTypeHandler()
    }
    return null
  }

}
